﻿#include "stdafx.h"
#include "common.h"
#include "FrameTime.h"
#include "MuNetConnect.h"

MuNetConnect *MuNetConnect::s_inst = nullptr;

MuNetConnect::MuNetConnect()
{
	m_clientVersion = "074";
	m_verificationString = "subv(*40ds7lkN2n";
	m_cs_ip = "127.0.0.1";
	m_cs_port = 44405;

    if (!m_encDec.ReadEnc_dat("Data/enc1.dat")) {
        Exit("Data/enc1.dat error!");
    }
    if (!m_encDec.ReadDec_dat("Data/Dec2.dat")) {
        Exit("Data/Dec2.dat error!");
    }

    m_cs.setId(m_cs_id);
    m_cs.setListener(this);
    m_gs.setId(m_gs_id);
    m_gs.setListener(this);

    uv_timer_init(uv_default_loop(), &m_timerTick);
    m_timerTick.data = this;
}


MuNetConnect::~MuNetConnect()
{
    uv_timer_stop(&m_timerTick);
}

size_t MuNetConnect::unpacket(u_char *data, size_t dataLen, u_char*& outPackData, size_t& outPackDataLen)
{
    if (dataLen < 3) return 0;

    size_t packLen;
    size_t packDataLen;
    u_char *packData;
    bool enc = false;
    switch (data[0])
    {
    case 0xc1:
        packLen = data[1];
        packData = (unsigned char *)data + 2;
        packDataLen = packLen - 2;
        break;
    case 0xc2:
        packLen = ntohs(*(WORD*)&data[1]);
        packData = (unsigned char *)data + 3;
        packDataLen = packLen - 3;
        break;
    case 0xc3:
        packLen = data[1];
        packData = (unsigned char *)data + 2;
        packDataLen = packLen - 2;
        enc = true;
        break;
    case 0xc4:
        packLen = ntohs(*(WORD*)&data[1]);
        packData = (unsigned char *)data + 3;
        packDataLen = packLen - 3;
        enc = true;
        break;
    default:
        Exit("网络封包错误: %x", *(int*)data);
        return 0;
    }
    if (dataLen >= packLen)
    {
        if (enc)
        {
            outPackDataLen = m_encDec.DecodeC3C4(m_tempBuf, packData, packDataLen);
            outPackData = m_tempBuf;
        }
        else {
            outPackDataLen = packDataLen;
            outPackData = packData;
        }
        return packLen;
    }
    return 0;
}

void MuNetConnect::connectCs(const char * host, u_short port)
{
    if (!m_cs.isConnected()) {
        m_cs.connect(host, port);
    }
}

void MuNetConnect::RequestServerList()
{
    u_char data[] = { 0xf4, 2 };
    send(m_cs, data, sizeof(data), false);
}

void MuNetConnect::RequestServerAddr(u_char code)
{
    u_char data[] = { 0xf4, 3, code };
    send(m_cs, data, sizeof(data), false);
}

void MuNetConnect::connectGs(const char * host, u_short port)
{
    if (!m_gs.isConnected()) {
        m_gs.connect(host, port);
    }
}

void MuNetConnect::Login(char * username, char * password)
{
    MuPack::Login data;
    data.t1 = 0xf1;
    data.t2 = 1;
    strncpy(data.username, username, 10);
    strncpy(data.password, password, 10);
    MuEncDec::Xor3Byte_fccfab((BYTE*)data.username, 10);
    MuEncDec::Xor3Byte_fccfab((BYTE*)data.password, 10);
    data.tickCount = FrameTime::GetInstance()->GetTickCount();
    memcpy(data.versionString, m_clientVersion, sizeof(data.versionString));
    memcpy(data.verificationString, m_verificationString, sizeof(data.verificationString));
    send(m_gs, &data, sizeof(data), true);
}

void MuNetConnect::RequestCharacterList()
{
    u_char data[] = { 0xf3, 0 };
    send(m_gs, data, sizeof(data), false);
}

MuNetConnect *MuNetConnect::GetInstance()
{
	if (!s_inst) s_inst = new MuNetConnect;
	return s_inst;
}

void MuNetConnect::Destroy()
{
	if (s_inst)
	{
		delete s_inst;
		s_inst = nullptr;
	}
}

void MuNetConnect::on_connect(TcpClient * sock, int status)
{
    if (!m_listener) return;
    sock->beginRead();
    if (sock->getId() == m_cs_id) {
        m_listener->on_cs_connect(status == 0);
    }
    else {
        m_listener->on_gs_connect(status == 0);
        setTick(true);
    }
}

void MuNetConnect::on_close(TcpClient * sock)
{
    if (!m_listener) return;
    if (sock->getId() == m_cs_id) {
        m_listener->on_cs_close();
    }
    else {
        m_listener->on_gs_close();
        setTick(false);
    }
}

size_t MuNetConnect::on_read(TcpClient * sock, char * data, size_t dataLen)
{
    size_t count = 0;
    size_t packLen;
    u_char* packData;
    size_t packDataLen;
    while (packLen = unpacket((u_char*)data + count, dataLen - count, packData, packDataLen))
    {
        count += packLen;
        if (m_listener) {
            if (sock->getId() == m_gs_id) {
                dispatchPacket_gs(packData, packDataLen);
            }
            else {
                dispatchPacket_cs(packData, packDataLen);
            }
        }
    }
    return count;
}

void MuNetConnect::send(TcpClient& sock, void * data, size_t dataLen, bool encode)
{
    static u_char sendBuf[0x10000];
    u_char* sendPos = nullptr;
    size_t sendLen = 0;
    assert(dataLen < 0xd000);
    if (encode) {
        dataLen = m_encDec.EncodeC3C4(sendBuf + 3, (unsigned char*)data, dataLen);
        if (dataLen > 253) {
            sendPos = sendBuf;
            sendLen = dataLen + 3;
            sendBuf[0] = 0xc4;
            sendBuf[1] = (u_char)(sendLen >> 8);
            sendBuf[2] = (u_char)sendLen;
        }
        else {
            sendPos = sendBuf + 1;
            sendLen = dataLen + 2;
            sendBuf[1] = 0xc3;
            sendBuf[2] = (u_char)sendLen;
        }
    }
    else {
        memcpy(sendBuf + 3, data, dataLen);
        if (dataLen > 253) {
            sendPos = sendBuf;
            sendLen = dataLen + 3;
            sendBuf[0] = 0xc2;
            sendBuf[1] = (u_char)(sendLen >> 8);
            sendBuf[2] = (u_char)sendLen;
        }
        else {
            sendPos = sendBuf + 1;
            sendLen = dataLen + 2;
            sendBuf[1] = 0xc1;
            sendBuf[2] = (u_char)sendLen;
        }
    }
    sock.write(sendPos, sendLen);
}

void MuNetConnect::dispatchPacket_cs(u_char * data, size_t dataLen)
{
    int t1, t2;

    t1 = data[0];
    t2 = data[1];
    switch (t1)
    {
    case 0:	//初次连接cs后收到
        //netPack.RequestServerList();
        m_listener->on_cs_firstResponse();
        return;
    case 0xf4:
        switch (t2)
        {
        case 2:	//收到服务器列表
        {
            m_listener->on_cs_serverList((MuPack::ServerList*)(data + 2));
            return;
        }
        case 3:	//选择线后收到的ip和port
            m_listener->on_cs_serverAddr((MuPack::ServerAddr*)(data + 2));
            return;
        }
    }
    dbgNum(t1, true);
    dbgNum(t2, true);
}

void MuNetConnect::dispatchPacket_gs(u_char * data, size_t dataLen)
{
    int t1, t2;

    t1 = data[0];
    t2 = data[1];
    switch (t1)
    {
    case 3:
    {
        WORD key = *(WORD*)(data + 2);
        //通过key计算客户端程序的校验和
        SendCheckSum(0);
        return;
    }
    case 0x10: //目标移动
        m_listener->on_gs_moveNotice((MuPack::MoveNotice*)(data + 1));
        return;
    case 0x12:	//周围玩家
        m_listener->on_gs_placePlayer((MuPack::PlacePlayer*)(data + 1));
        return;
    case 0x13:	//周围怪物
        m_listener->on_gs_placeMonster((MuPack::PlaceMonster*)(data + 1));
        return;
    case 0x14:	//超出范围，移除目标
        m_listener->on_gs_removeTarget((MuPack::RemoveTarget*)(data + 1));
        return;
    case 0x18:	//面对方向改变或者物理攻击
        m_listener->on_gs_changeDir((MuPack::ChangeDir *)(data + 1));
        return;
    case 0x26:
        switch (t2)
        {
        case 0xff:
            m_listener->on_gs_setCurrLife(ntohs(*(WORD*)(data + 2)));
            return;
        default:
            break;
        }
        break;
    case 0x27:
        switch (t2)
        {
        case 0xff:
            m_listener->on_gs_setCurrMana(ntohs(*(WORD*)(data + 2)));
            return;
        default:
            break;
        }
        break;
    case 0xf1:
        switch (t2)
        {
        case 0:	//收到GameObjId和版本信息
            m_listener->on_gs_gameObjIdAndVersion((MuPack::GameObjIdAndVersion*)(data + 2));
            return;
        case 1:	//登录验证结果
            m_listener->on_gs_loginResult(data[2]);
            return;
        }
        break;
    case 0xf3:
        switch (t2)
        {
        case 0: //角色列表
            m_listener->on_gs_characterList((MuPack::CharacterList*)(data + 2));
            return;
        case 3:	//人物属性
            m_listener->on_gs_characterInfo((MuPack::CharacterInfo*)(data + 2));
            return;
        case 0x10:	//装备数据
            return;
        case 0x11:	//技能信息
            return;
        }
        break;
    }
    dbgNum(t1, true);
    dbgNum(t2, true);
}

void MuNetConnect::CreateCharacter(char * name, MuType::CharacterClass mucls)
{
    u_char data[13] = { 0xf3, 1 };
    memcpy(data + 2, name, 10);
    data[12] = mucls * 16;
    send(m_gs, data, sizeof(data), false);
}

void MuNetConnect::DeleteCharacter(char * name)
{
}

void MuNetConnect::StartPlaying(char * name)
{
    u_char data[12] = { 0xf3, 3 };
    memcpy(data + 2, name, 10);
    send(m_gs, data, sizeof(data), false);
}

void MuNetConnect::SendCheckSum(int key)
{
    u_char data[6] = { 3, 0 };
    *(int*)(data + 2) = key;
    send(m_gs, data, sizeof(data), true);
}

void MuNetConnect::SendTick()
{
    static DWORD t = 0;
    MuPack::Tick tick;
    tick.t1 = 0xe;
    tick.t2 = 0;
    tick.tick = t;	// FrameTime::GetInstance()->GetTickCount();
    t += 20000;
    send(m_gs, &tick, sizeof(tick), false);
}

void MuNetConnect::SendMapDir(u_char dir)
{
    u_char data[] = { 0x18, dir, 0x66 };
    send(m_gs, data, sizeof(data), false);
}

void MuNetConnect::SendMovePosDirs(POINT & start, std::vector<u_char>& dirs)
{
    u_char data[16] = { 0x10, (u_char)start.x, (u_char)start.y };
    int dirCount = dirs.size();
    if (dirCount > 15) dirCount = 15;
    auto pDir = &dirs[0];
    data[3] = (pDir[dirCount-1] << 4) | dirCount;
    auto pData = data + 4;
    size_t i = 0;
    for (; i + 1 < dirCount; i += 2) {
        *pData = (*pDir << 4) | (pDir[1] & 0xf);
        pDir += 2;
        pData++;
    }
    if (i < dirCount) {
        *pData = (*pDir << 4);
        pData++;
    }
    send(m_gs, data, pData - data, false);
}

void MuNetConnect::Attack(u_short id, u_char dir)
{
    u_char data[] = { 0x15, (u_char)(id>>8), (u_char)(id & 0xff), 0x64, dir };
    send(m_gs, data, sizeof(data), false);
}

void MuNetConnect::setTick(bool run)
{
    if (run) {
        uv_timer_start(&m_timerTick, on_timer, 0, 20000);
    }
    else {
        uv_timer_stop(&m_timerTick);
    }
}

void MuNetConnect::on_timer(uv_timer_t * handle)
{
    auto _this = (MuNetConnect*)handle->data;
    _this->SendTick();
}